/**
 **********************************************************************************************************************
 * @file    menu_frame.c
 * @brief   该文件提供菜单框架功能
 *
 * @details  功能详细说明：
 *           + 菜单初始化函数
 *           + 返回主菜单函数
 *           + 菜单控制函数
 *           + 菜单轮询任务函数
 **********************************************************************************************************************
 * 使用方式:
 *    1、使用前初始化函数 MMI_MenuInit, 设置主菜单内容
 *    2、周期调用函数 MMI_MenuTask, 用来处理菜单显示和执行相关回调函数
 *    3、使用其他函数对菜单界面控制
 **********************************************************************************************************************
 */

#include "menu_frame.h"

#ifdef MMI_MENU_USE_SHORTCUT
#include <stdarg.h>
#endif

typedef struct ST_MENU_CTL
{
    struct ST_MENU_CTL *pParentMenuCtrl; /*!< 父菜单控制处理 */
    UN_MENU_DESC uMenuDesc;              /*!< 当前菜单的字符串描述 */
    void *pExtendData;                   /*!< 当前选项注册时的扩展数据 */
    PF_MENU_SHOW_CB pfnShowCallback;     /*!< 当前菜单显示效果函数 */
    ST_MENU_LIST *pMenuList;             /*!< 当前菜单列表 */
    PF_MENU_LOAD_CB pfnLoadCallback;     /*!< 当前菜单加载函数 */
    PF_MENU_TASK_CB pfnTaskCallback;     /*!< 当前选项的非菜单功能函数 */
    uint8_t itemsNum;                    /*!< 当前菜单选项总数目 */
    uint8_t showBaseItem;                /*!< 当前菜单首个显示的选项 */
    uint8_t selectItem;                  /*!< 当前菜单选中的选项 */
    bool isSelected;                     /*!< 菜单选项是否已经被选择 */
} ST_MENU_CTL;

typedef struct
{
    ST_MENU_CTL *pMenuCtrl;                /*!< 当前菜单控制处理 */
    PF_MENU_ENTER_CB pfnMainEnterCallback; /*!< 主菜单进入时(进入菜单)需要执行一次的函数 */
    PF_MENU_EXIT_CB pfnMainExitCallback;   /*!< 主菜单进入后退出时(退出菜单)需要执行一次的函数 */
    PF_MENU_LOAD_CB pfnLoadCallback;       /*!< 重加载函数 */
    uint8_t isEnterMainMenu : 1;           /*!< 是否进入了主菜单 */
} ST_MENU_MANAGE;

static ST_MENU_MANAGE gstMenuManage;
static ST_MENU_CTL gastMenuCtrl[MMI_MENU_MAX_DEPTH];
static uint8_t gCurrMenuDepth;

/**
 * @brief      主菜单进入
 * @return     0,成功; -1,失败
 */
int MMI_MainMenuEnter(void)
{
    int i;
    ST_MENU_LIST *pMenuList;
    ST_MENU_SHOW tMenuShow;

    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 1)
        return -1;

    if (gstMenuManage.pfnMainEnterCallback != NULL)
    {
        ST_MENU_ITEM_INFO tItemInfo;

        tItemInfo.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;
        gstMenuManage.pfnMainEnterCallback(&tItemInfo);
    }

    gstMenuManage.isEnterMainMenu = 1;
    gstMenuManage.pfnLoadCallback = gstMenuManage.pMenuCtrl->pfnLoadCallback;

    if (gstMenuManage.pMenuCtrl->pMenuList != NULL)
    {
        pMenuList = gstMenuManage.pMenuCtrl->pMenuList;
        tMenuShow.itemsNum = gstMenuManage.pMenuCtrl->itemsNum;
        tMenuShow.selectItem = gstMenuManage.pMenuCtrl->selectItem;
        tMenuShow.showBaseItem = gstMenuManage.pMenuCtrl->showBaseItem;
        tMenuShow.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tMenuShow.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;

        for (i = 0; i < tMenuShow.itemsNum && i < MMI_MENU_MAX_NUMBER; i++)
        {
            tMenuShow.uItemsListDesc[i] = pMenuList[i].uMenuDesc;
            tMenuShow.pItemsListExtendData[i] = pMenuList[i].pExtendData;
        }

        if (gstMenuManage.pMenuCtrl->pfnShowCallback != NULL)
        {
            gstMenuManage.pMenuCtrl->pfnShowCallback(&tMenuShow);
        }

        gstMenuManage.pMenuCtrl->showBaseItem = tMenuShow.showBaseItem;
    }

    return 0;
}

/**
 * @brief      主菜单退出
 * @attention  不管处于任何界面都会逐级退出到主菜单后（会调用退出函数），再退出主菜单
 * @return     0,成功; -1,失败
 */
int MMI_MainMenuExit()
{
    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 0)
        return -1;

    while (MMI_MenuExit(true) == 0)
    {
    }

    if (gstMenuManage.pfnMainExitCallback != NULL)
    {
        gstMenuManage.pfnMainExitCallback(NULL);
    }
    gstMenuManage.isEnterMainMenu = 0;

    return 0;
}

/**
 * @brief      新建菜单层级
 * @return     ST_MENU_CTL*
 */
static ST_MENU_CTL *MMI_MenuNew(void)
{
    ST_MENU_CTL *pMenuCtrl = NULL;

    if (gCurrMenuDepth < MMI_MENU_MAX_DEPTH)
    {
        pMenuCtrl = &gastMenuCtrl[gCurrMenuDepth];
        gCurrMenuDepth++;
    }

    return pMenuCtrl;
}

/**
 * @brief      销毁菜单层级
 * @param      pMenu
 */
static void MMI_MenuDelete(ST_MENU_CTL *pMenu)
{
    if (gCurrMenuDepth > 0)
    {
        gCurrMenuDepth--;
    }
}

/**
 * @brief      菜单初始化
 * @param[in]  pMainMenu        主菜单注册信息
 * @return     0,成功; -1,失败
 */
int MMI_MenuInit(ST_MENU_CFG *pMainMenu)
{
    ST_MENU_CTL *pNewMenuCtrl = NULL;

    if (gstMenuManage.pMenuCtrl != NULL)
        return -1;

#if MMI_MENU_MAX_DEPTH != 0
    gCurrMenuDepth = 0;
#endif

    if ((pNewMenuCtrl = MMI_MenuNew()) == NULL)
        return -1;

    pNewMenuCtrl->uMenuDesc = pMainMenu->uMenuDesc;
    pNewMenuCtrl->pExtendData = pMainMenu->pExtendData;
    pNewMenuCtrl->pParentMenuCtrl = NULL;
    pNewMenuCtrl->pfnLoadCallback = pMainMenu->pfnLoadCallback;
    pNewMenuCtrl->pfnShowCallback = NULL;
    pNewMenuCtrl->pfnTaskCallback = pMainMenu->pfnTaskCallback;

    pNewMenuCtrl->pMenuList = NULL;
    pNewMenuCtrl->itemsNum = 0;
    pNewMenuCtrl->selectItem = 0;
    pNewMenuCtrl->showBaseItem = 0;

    gstMenuManage.pMenuCtrl = pNewMenuCtrl;
    gstMenuManage.isEnterMainMenu = 0;
    gstMenuManage.pfnMainEnterCallback = pMainMenu->pfnEnterCallback;
    gstMenuManage.pfnMainExitCallback = pMainMenu->pfnExitCallback;
    gstMenuManage.pfnLoadCallback = pNewMenuCtrl->pfnLoadCallback;

    return 0;
}

/**
 * @brief  菜单反初始化
 * @attention  不管处于任何界面都会逐级退出到主菜单后（会调用退出函数），再退出主菜单，最后反初始化
 * @return 0,成功; -1,失败
 */
int MMI_MenuDeinit(void)
{
    if (gstMenuManage.pMenuCtrl == NULL)
        return -1;

    MMI_MainMenuExit();

    MMI_MenuDelete(gstMenuManage.pMenuCtrl);
    gstMenuManage.pMenuCtrl = NULL;
    gstMenuManage.isEnterMainMenu = 0;
    gstMenuManage.pfnMainEnterCallback = NULL;
    gstMenuManage.pfnMainExitCallback = NULL;
    gstMenuManage.pfnLoadCallback = NULL;

    return 0;
}

/**
 * @brief      子菜单绑定当前菜单选项
 * @param      pMenuList       新的菜单列表
 * @param      menuNum         新的菜单列表数目
 * @param      pfnShowCallback  新的菜单列表显示效果回调函数, 为NULL则延续上级菜单显示效果
 * @return     0,成功; -1,失败
 */
int MMI_MenuBind(ST_MENU_LIST *pMenuList, uint8_t menuNum, PF_MENU_SHOW_CB pfnShowCallback)
{
    if (gstMenuManage.pMenuCtrl == NULL)
        return -1;

    if (gstMenuManage.pMenuCtrl->pMenuList != NULL)
        return 0;

    gstMenuManage.pMenuCtrl->pMenuList = pMenuList;
    gstMenuManage.pMenuCtrl->itemsNum = menuNum;

    if (pfnShowCallback != NULL)
    {
        gstMenuManage.pMenuCtrl->pfnShowCallback = pfnShowCallback;
    }

    return 0;
}

/**
 * @brief      复位菜单, 回到主菜单界面
 * @note       该复位回到主菜单不会执行退出所需要执行的回调函数
 * @return     0,成功; -1,失败
 */
int MMI_MenuReset(void)
{
    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 0)
        return -1;

    while (gstMenuManage.pMenuCtrl->pParentMenuCtrl != NULL)
    {
        ST_MENU_CTL *pMenuCtrl = gstMenuManage.pMenuCtrl;

        gstMenuManage.pMenuCtrl = gstMenuManage.pMenuCtrl->pParentMenuCtrl;
        MMI_MenuDelete(pMenuCtrl);
    }
    gstMenuManage.pMenuCtrl->selectItem = 0;

    return 0;
}

/**
 * @brief      进入当前菜单选项
 * @return     0,成功; -1,失败
 */
int MMI_MenuEnter(void)
{
    ST_MENU_CTL *pNewMenuCtrl = NULL;
    ST_MENU_CTL *pCurrMenuCtrl = gstMenuManage.pMenuCtrl;

    if (gstMenuManage.pMenuCtrl == NULL ||
        gstMenuManage.pMenuCtrl->pMenuList == NULL ||
        gstMenuManage.isEnterMainMenu == 0)
    {
        return -1;
    }

    if ((pNewMenuCtrl = MMI_MenuNew()) == NULL)
        return -1;

    pNewMenuCtrl->uMenuDesc = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].uMenuDesc;
    pNewMenuCtrl->pExtendData = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pExtendData;
    pNewMenuCtrl->pMenuList = NULL;
    pNewMenuCtrl->itemsNum = 0;
    pNewMenuCtrl->pfnShowCallback = pCurrMenuCtrl->pfnShowCallback;
    pNewMenuCtrl->pfnLoadCallback = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnLoadCallback;
    pNewMenuCtrl->pfnTaskCallback = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnTaskCallback;
    pNewMenuCtrl->selectItem = 0;
    pNewMenuCtrl->isSelected = true;
    pNewMenuCtrl->pParentMenuCtrl = pCurrMenuCtrl;

    gstMenuManage.pMenuCtrl = pNewMenuCtrl;
    gstMenuManage.pfnLoadCallback = pNewMenuCtrl->pfnLoadCallback;

    if (pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallback != NULL)
    {
        ST_MENU_ITEM_INFO tItemInfo;

        tItemInfo.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;
        pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallback(&tItemInfo);
    }

    return 0;
}

/**
 * @brief      退出当前选项并返回上一层菜单
 * @param[in]  isReset 菜单选项是否从头选择
 * @return     0,成功; -1,失败, 即目前处于主菜单, 无法返回
 */
int MMI_MenuExit(bool isReset)
{
    ST_MENU_CTL *pMenuCtrl = gstMenuManage.pMenuCtrl;

    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (gstMenuManage.pMenuCtrl->pParentMenuCtrl == NULL)
        return -1;

    gstMenuManage.pMenuCtrl = gstMenuManage.pMenuCtrl->pParentMenuCtrl;
    gstMenuManage.pfnLoadCallback = gstMenuManage.pMenuCtrl->pfnLoadCallback;
    MMI_MenuDelete(pMenuCtrl);
    pMenuCtrl = NULL;

    if (gstMenuManage.pMenuCtrl->pMenuList[gstMenuManage.pMenuCtrl->selectItem].pfnExitCallback != NULL)
    {
        ST_MENU_ITEM_INFO tItemInfo;

        tItemInfo.uMenuDesc = gstMenuManage.pMenuCtrl->pMenuList[gstMenuManage.pMenuCtrl->selectItem].uMenuDesc;
        tItemInfo.pExtendData = gstMenuManage.pMenuCtrl->pMenuList[gstMenuManage.pMenuCtrl->selectItem].pExtendData;
        gstMenuManage.pMenuCtrl->isSelected = false;
        gstMenuManage.pMenuCtrl->pMenuList[gstMenuManage.pMenuCtrl->selectItem].pfnExitCallback(&tItemInfo);
    }

    if (isReset)
    {
        gstMenuManage.pMenuCtrl->selectItem = 0;
    }

    return 0;
}

/**
 * @brief      选择上一个菜单选项
 * @param[in]  isAllowRoll 第一个选项时是否从跳转到最后一个选项
 * @return     0,成功; -1,失败
 */
int MMI_MenuSelectPrev(bool isAllowRoll)
{
    if (gstMenuManage.pMenuCtrl == NULL ||
        gstMenuManage.pMenuCtrl->pMenuList == NULL ||
        gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (gstMenuManage.pMenuCtrl->selectItem > 0)
    {
        gstMenuManage.pMenuCtrl->selectItem--;
    }
    else
    {
        if (isAllowRoll)
        {
            gstMenuManage.pMenuCtrl->selectItem = gstMenuManage.pMenuCtrl->itemsNum - 1;
        }
        else
        {
            gstMenuManage.pMenuCtrl->selectItem = 0;
        }
    }

    return 0;
}

/**
 * @brief      选择下一个菜单选项
 * @param[in]  isAllowRoll 最后一个选项时是否跳转到第一个选项
 * @return     0,成功; -1,失败
 */
int MMI_MenuSelectNext(bool isAllowRoll)
{
    if (gstMenuManage.pMenuCtrl == NULL ||
        gstMenuManage.pMenuCtrl->pMenuList == NULL ||
        gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (gstMenuManage.pMenuCtrl->selectItem < (gstMenuManage.pMenuCtrl->itemsNum - 1))
    {
        gstMenuManage.pMenuCtrl->selectItem++;
    }
    else
    {
        if (isAllowRoll)
        {
            gstMenuManage.pMenuCtrl->selectItem = 0;
        }
        else
        {
            gstMenuManage.pMenuCtrl->selectItem = gstMenuManage.pMenuCtrl->itemsNum - 1;
        }
    }

    return 0;
}

/**
 * @brief      选择指定的菜单选项
 * @param      selectItem 指定的菜单选项
 * @return     0,成功; -1,失败
 */
int MMI_MenuSelect(uint8_t selectItem)
{
    if (gstMenuManage.pMenuCtrl == NULL ||
        gstMenuManage.pMenuCtrl->pMenuList == NULL ||
        gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (selectItem >= gstMenuManage.pMenuCtrl->itemsNum)
        return -1;

    gstMenuManage.pMenuCtrl->selectItem = selectItem;

    return 0;
}

#ifdef MMI_MENU_USE_SHORTCUT

/**
 * @brief      相对主菜单或当前菜单通过下级各菜单索引快速进入指定选项
 * @param[in]  isAbsolute 是否采用绝对菜单索引（从主菜单开始）
 * @param[in]  deep 菜单深度，大于 0
 * @param[in]  ...  各级菜单索引值(从0开始), 入参个数由 deep 的值决定
 * @return     0,成功; -1,失败
 */
int MMI_MenuShortcutEnter(bool isAbsolute, uint8_t deep, ...)
{
    uint8_t selectDeep = 0;
    va_list pItemList;
    uint8_t selectItem;

    if (gstMenuManage.pMenuCtrl == NULL ||
        gstMenuManage.pMenuCtrl->pMenuList == NULL ||
        gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (isAbsolute)
        MMI_MenuReset();

    va_start(pItemList, deep);

    while (selectDeep < deep)
    {
        selectItem = va_arg(pItemList, int);
        if (selectItem >= gstMenuManage.pMenuCtrl->itemsNum)
        {
            va_end(pItemList);
            return -1;
        }

        gstMenuManage.pMenuCtrl->selectItem = selectItem;
        MMI_MenuEnter();
        selectDeep++;
    }

    va_end(pItemList);

    return 0;
}
#endif

/**
 * @brief      限制当前菜单界面最多显示的菜单数目
 * @note       在菜单显示效果回调函数中使用, 使用成员变量 showBaseItem 得到显示界面的第一个选项索引
 * @param[in,out]  tMenuShow   当前菜单显示信息
 * @param[in,out]  showNum     当前菜单中需要显示的选项数目, 根据当前菜单选项的总数得到最终的显示的选项数目
 * @return     0,成功; -1,失败
 */
int MMI_MenuLimitShowListNumber(ST_MENU_SHOW *ptMenuShow, uint8_t *pShowNum)
{
    if (ptMenuShow == NULL || pShowNum == NULL)
        return -1;

    if (*pShowNum > ptMenuShow->itemsNum)
    {
        *pShowNum = ptMenuShow->itemsNum;
    }

    if (ptMenuShow->selectItem < ptMenuShow->showBaseItem)
    {
        ptMenuShow->showBaseItem = ptMenuShow->selectItem;
    }
    else if ((ptMenuShow->selectItem - ptMenuShow->showBaseItem) >= *pShowNum)
    {
        ptMenuShow->showBaseItem = ptMenuShow->selectItem - *pShowNum + 1;
    }
    else
    {
        // 保持
    }

    return 0;
}

/**
 * @brief       获取当前父菜单显示信息
 *              如获取当前菜单的二级父菜单信息，level 为2
 * @param[out]  ptMenuShow 父 n 级菜单显示信息
 * @param[in]   level      n 级, 大于 0
 * @return      0,成功; -1,失败
 */
int MMI_MenuQueryParentMenu(ST_MENU_SHOW *ptMenuShow, uint8_t level)
{
    int i;
    ST_MENU_LIST *pMenu;
    ST_MENU_CTL *pMenuCtrl = NULL;

    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 0)
        return -1;

    pMenuCtrl = gstMenuManage.pMenuCtrl->pParentMenuCtrl;

    while (level && pMenuCtrl != NULL)
    {
        pMenu = pMenuCtrl->pMenuList;
        ptMenuShow->itemsNum = pMenuCtrl->itemsNum;
        ptMenuShow->selectItem = pMenuCtrl->selectItem;
        ptMenuShow->showBaseItem = pMenuCtrl->showBaseItem;
        ptMenuShow->uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;

        for (i = 0; i < ptMenuShow->itemsNum && i < MMI_MENU_MAX_NUMBER; i++)
        {
            ptMenuShow->uItemsListDesc[i] = pMenu[i].uMenuDesc;
            ptMenuShow->pItemsListExtendData[i] = pMenu[i].pExtendData;
        }

        pMenuCtrl = pMenuCtrl->pParentMenuCtrl;
        level--;
    }

    if (level != 0 && pMenuCtrl == NULL)
        return -1;

    return 0;
}

/**
 * @brief  菜单任务
 * @return 0,成功, 处于菜单模式下; -1,失败, 未处于菜单模式下
 */
int MMI_MenuTask(const ST_KEY_INFO *pKeyInfo)
{
    int i;
    ST_MENU_LIST *pMenuList;
    ST_MENU_SHOW tMenuShow;

    if (gstMenuManage.pMenuCtrl == NULL || gstMenuManage.isEnterMainMenu == 0)
        return -1;

    if (gstMenuManage.pfnLoadCallback != NULL)
    {
        ST_MENU_ITEM_INFO tItemInfo;

        tItemInfo.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;
        gstMenuManage.pfnLoadCallback(&tItemInfo);
        gstMenuManage.pfnLoadCallback = NULL;
    }

    if (gstMenuManage.pMenuCtrl->pMenuList != NULL)
    {
        pMenuList = gstMenuManage.pMenuCtrl->pMenuList;
        tMenuShow.itemsNum = gstMenuManage.pMenuCtrl->itemsNum;
        tMenuShow.selectItem = gstMenuManage.pMenuCtrl->selectItem;
        tMenuShow.showBaseItem = gstMenuManage.pMenuCtrl->showBaseItem;
        tMenuShow.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tMenuShow.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;

        for (i = 0; i < tMenuShow.itemsNum && i < MMI_MENU_MAX_NUMBER; i++)
        {
            tMenuShow.uItemsListDesc[i] = pMenuList[i].uMenuDesc;
            tMenuShow.pItemsListExtendData[i] = pMenuList[i].pExtendData;
        }

        if (gstMenuManage.pMenuCtrl->pfnShowCallback != NULL)
        {
            gstMenuManage.pMenuCtrl->pfnShowCallback(&tMenuShow);
        }

        gstMenuManage.pMenuCtrl->showBaseItem = tMenuShow.showBaseItem;
    }

    if (gstMenuManage.pMenuCtrl->pfnTaskCallback != NULL)
    {
        ST_MENU_ITEM_INFO tItemInfo;

        tItemInfo.uMenuDesc = gstMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = gstMenuManage.pMenuCtrl->pExtendData;
        gstMenuManage.pMenuCtrl->pfnTaskCallback(pKeyInfo, &tItemInfo);
    }

    return 0;
}
